/*
 * Copyright 2020 by OLTPBenchmark Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.oltpbenchmark.benchmarks.seats.util;

import com.oltpbenchmark.benchmarks.seats.SEATSConstants;
import com.oltpbenchmark.util.CompositeId;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.Objects;

public final class FlightId extends CompositeId implements Comparable<FlightId> {

  private static final int[] COMPOSITE_BITS = {
    LONG_MAX_DIGITS, // AIRLINE_ID
    LONG_MAX_DIGITS, // DEPART AIRPORT_ID
    LONG_MAX_DIGITS, // ARRIVE AIRPORT_ID
    LONG_MAX_DIGITS // DEPART DATE
  };

  /** The airline for this flight */
  private long airline_id;

  /** The id of the departure airport */
  private long depart_airport_id;

  /** The id of the arrival airport */
  private long arrive_airport_id;

  /** This is the departure time of the flight in minutes since the benchmark start date */
  private long depart_date;

  /**
   * Constructor
   *
   * @param airline_id - The airline for this flight
   * @param depart_airport_id - the id of the departure airport
   * @param arrive_airport_id - the id of the arrival airport
   * @param benchmark_start - the base date of when the benchmark data starts (including past days)
   * @param flight_date - when departure date of the flight
   */
  public FlightId(
      long airline_id,
      long depart_airport_id,
      long arrive_airport_id,
      Timestamp benchmark_start,
      Timestamp flight_date) {
    this.airline_id = airline_id;
    this.depart_airport_id = depart_airport_id;
    this.arrive_airport_id = arrive_airport_id;
    this.depart_date = FlightId.calculateFlightDate(benchmark_start, flight_date);
  }

  /**
   * Constructor. Converts a composite id generated by encode() into the full object
   *
   * @param composite_id
   */
  public FlightId(String composite_id) {
    this.set(composite_id);
  }

  public void set(String composite_id) {
    this.decode(composite_id);
  }

  @Override
  public String encode() {
    return (this.encode(COMPOSITE_BITS));
  }

  @Override
  public void decode(String composite_id) {
    String[] values = super.decode(composite_id, COMPOSITE_BITS);
    this.airline_id = Long.parseLong(values[0]);
    this.depart_airport_id = Long.parseLong(values[1]);
    this.arrive_airport_id = Long.parseLong(values[2]);
    this.depart_date = Long.parseLong(values[3]);
  }

  @Override
  public String[] toArray() {
    return (new String[] {
      Long.toString(this.airline_id),
      Long.toString(this.depart_airport_id),
      Long.toString(this.arrive_airport_id),
      Long.toString(this.depart_date)
    });
  }

  /**
   * @param benchmark_start
   * @param flight_date
   * @return
   */
  protected static long calculateFlightDate(Timestamp benchmark_start, Timestamp flight_date) {
    return (flight_date.getTime() - benchmark_start.getTime()) / 3600000; // 60s * 60m * 1000
  }

  /**
   * @return the id
   */
  public long getAirlineId() {
    return airline_id;
  }

  /**
   * @return the depart_airport_id
   */
  public long getDepartAirportId() {
    return depart_airport_id;
  }

  /**
   * @return the arrive_airport_id
   */
  public long getArriveAirportId() {
    return arrive_airport_id;
  }

  public long getDepartDate() {
    return depart_date;
  }

  /**
   * @return the flight departure date
   */
  public Timestamp getDepartDateAsTimestamp(Timestamp benchmark_start) {
    return (new Timestamp(
        benchmark_start.getTime()
            + (this.depart_date * SEATSConstants.MILLISECONDS_PER_MINUTE * 60)));
  }

  public boolean isUpcoming(Timestamp benchmark_start, long past_days) {
    Timestamp depart_date = this.getDepartDateAsTimestamp(benchmark_start);
    return ((depart_date.getTime() - benchmark_start.getTime())
        >= (past_days * SEATSConstants.MILLISECONDS_PER_DAY));
  }

  @Override
  public String toString() {
    return String.format(
        "FlightId{airline=%d,depart=%d,arrive=%d,date=%s}",
        this.airline_id, this.depart_airport_id, this.arrive_airport_id, this.depart_date);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    FlightId flightId = (FlightId) o;
    return airline_id == flightId.airline_id
        && depart_airport_id == flightId.depart_airport_id
        && arrive_airport_id == flightId.arrive_airport_id
        && depart_date == flightId.depart_date;
  }

  @Override
  public int hashCode() {
    return Objects.hash(airline_id, depart_airport_id, arrive_airport_id, depart_date);
  }

  @Override
  public int compareTo(FlightId o) {
    return Comparator.comparingLong(FlightId::getAirlineId)
        .thenComparingLong(FlightId::getDepartAirportId)
        .thenComparingLong(FlightId::getArriveAirportId)
        .thenComparingLong(FlightId::getDepartDate)
        .compare(this, o);
  }
}
